Go语言简介

Author Avatar
子语 2018 - 01 - 02
  • 在其它设备中阅读本文章

用Go解决现有编程难题

开发速度

编译一个大型的C或C++项目需要花费较长的时间。而Go使用了更为智能的编译器,简化了解决依赖的算法,最终提高了编译速度。编译Go程序时,编译器只会关注那些被直接引用的库,而不像Java或C那样遍历依赖链中所有依赖的库。

因为没有从编译代码到执行代码的中间过程,用动态语言编写应用程序可以快速看到输出,代价是动态语言不提供静态语言提供的类型安全特性,因此经常需要使用大量的测试套件来避免运行时出现类型错误这类bug。

并发

Go语言对并发的支持是这门语言最重要的特性之一。goroutine很像线程,但它占据的内存远少于线程,且使用它需要的代码更少。channel(通道)是一种内置的数据结构,可以让不同用户在不同的goroutine之间同步发送具有类型的消息,这让编程模型更倾向于在goroutine间发送消息,而不是让多个goroutine争夺同一个数据的试用权。

goroutine

goroutine是可以与其它goroutine并行执行的函数,同时也会与主程序并行执行。在其他编程代码中,需要使用线程来完成同样的事,而在Go中会使用同一个线程来执行多个goroutine。例如:用户在写一个Web服务器,希望同时处理不同的Web请求,Java需要写大量额外的代码来使用线程。而在Go中,net/http库直接使用了内置的goroutine,每个请求都自动在自己的goroutine里处理。Go运行时会自动在配置的一组逻辑处理器上调度运行goroutine,每个逻辑处理器绑定到一个操作系统线程上,这让应用程序执行效率更高,而开发工作量减少。

如果想在执行一段代码时,并行去做另外的事,goroutine是很好的选择,下面是一个简单的例子:

func log(msg string) {
  // 此处是处理日志的代码
}

// 代码中有些地方检测到错误
go log("发生错误")

关键字go是唯一需要去编写的代码,调用log()作为独立的goroutine去运行,以便与其他goroutine并行执行,这意味着应用程序的其他部分会与记录日志并行执行。

通道

通道是一种数据结构,可以让goroutine之间安全的通信,可以避免其他语言中常见的共享内存访问的问题。

并发最难的部分就是要确保其他并发运行的进程、线程或goroutine不会意外修改用户的数据。当不同线程在没有同步保护的情况下修改同一个数据时,将会导致错误。在其他语言中如果使用全局变量或共享内存,需要使用复杂的锁规则来防止对同一个变量的不同步修改。

通道提供了新模式,这一模式保证同一时刻只会有一个goroutine修改数据。通道用于在几个goroutine之间发哦那个数据。例子:一个应用程序,有多个进程需要顺序读取或修改某个数据,使用goroutine和通道可以为这个过程建立安全的模型。运行过程如下图所示:

无法加载

上图有三个goroutine,还有2个不带缓存的通道。第一个goroutine通过通道将数据传给已经在等待的第二个goroutine。两个goroutine间传输数据是同步的,一旦传输完成,两者都会知道数据完成传输。第二个goroutinelion个数据完成任务后,会将数据传给第三个goroutine,传输依然是同步的。这种在goroutine之间安全传输数据的方法不需要任何锁或者同步机制。

需要注意的是,通道不提供跨goroutine的数据访问保护机制。如果通过通道传输数据的一份副本,那么每个goroutine都会持有一份副本,各自对自己的副本做修改是安全的。当传输指向数据的指针时,如果读和写是由不同的goroutine完成,每个goroutine依旧需要额外的同步动作。

Go的数据类型

Go提供灵活的、无继承的类型系统,无需降低运行性能就能最大程度复用代码,这个类型依然支持面向对象开发。Go使用组合composition设计模式,只需要简单地将一个类型嵌入另一个类型,就能复用所有的功能。

此外Go还有独特的接口实现机制,允许用户对行为进行建模,而不是对类型进行建模。在Go中不需要声明某个类型实现某个接口,编译器会判断类型的实例是否符合正在使用的接口。

类型简单

Go不仅有intstring这样的内置类型,还支持自定义类型。Go中自定义的类型通常包含一组带类型的子弹,用于存储数据,类似于C语言的结构,但Go的类型可以声明操作该类型数据的方法。传统语言使用继承来扩展结构,而Go构建更小的开发类型,然后将这些小类型组合为更大的类型。

Go接口对一组行为建模

接口用于描述类型的行为,如果一个类型的实例实现了一个接口,意味着这个实例可以执行一组特地的行为。你甚至不要去声明这个实例实现了某个接口,只需要实现这组行为就好了,这种特性被称为鸭子特性。Go中如果一个类型实现了一个接口所有方法,那么这个类型的实例就可以存储在这个接口类型的实例中,不需要额外声明。

Go的接口一般只会描述一个单一的动作,在Go中最常用的接口之一就是io.Reader,该接口提供了一个简单的方法,用来声明一个类型有数据可以读取,其定义如下:

type Reader interface {
  Read(p []byte) (n int, err error)
}

为了实现该接口,你只需要实现一个Read方法,这个方法接受一个byte切片,返回一个整数和可能出现的错误。

这个与传统接口有本质区别,更有利于使用组合来复用代码,用户几乎可以给所有包含数据的类型实现io.Reader,然后把这个类型的实例传给任何一个知道如何读取io.Reader的Go函数。

内存管理

不当的内存管理会导致程序崩溃或者内存泄漏,甚至系统崩溃。Go具有现代化的垃圾回收机制。其他语言使用内存前要先分配这段内存,然后使用完毕后释放。哪怕出现一点失误,就会导致程序崩溃或内存泄漏。这存在一个问题,追踪内存是否还在被使用是件艰难的事,而要想支持多线程和高并发,更是让这件事愈发困难。虽然Go的垃圾回收会造成额外开销,但降低了开发难度。

Hello ,Go

package main     // Go程序都组织成包

import "fmt"     // 导入外部代码,fmt包用于格式化并输出数据

func main()  {   // main函数是程序的入口
	fmt.Println("Hello World!")
}			

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Go/Go Base/Go语言简介/